﻿using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;

namespace MetadataDesignerLibrary
{
    public class MethodDefinitionRule : ILRule<MethodDefinitionHandle>
    {
        [ILInfo(true)]
        public string Name { get; }

        [ILInfo]
        public MethodAttributes Attributes { get; }

        [ILInfo]
        public MethodImplAttributes ImplAttributes { get; }

        [ILInfo(ILInfoParseMethod.AsEntityHandle)]
        public TypeDefinitionHandle DeclaringType { get; }

        [ILInfo(true, ILInfoParseMethod.AsEntityHandleCollection)]
        public ParameterHandleCollection Parameters { get; }

        [ILInfo(true, ILInfoParseMethod.AsEntityHandleCollection)]
        public GenericParameterHandleCollection GenericParameters { get; }

        [ILInfo(ILInfoParseMethod.AsEntityHandleCollection)]
        public CustomAttributeHandleCollection CustomAttributes { get; }

        [ILInfo(ILInfoParseMethod.AsEntityHandleCollection)]
        public DeclarativeSecurityAttributeHandleCollection DeclarativeSecurityAttributes { get; }

        [ILInfo]
        public int RelativeVirtualAddress { get; }

        public MethodImport Import { get; }
        public byte[] Signature { get; }
        public int BodyOffset { get; }

        public MethodDefinitionRule(
            MethodAttributes attributes,
            MethodImplAttributes implAttributes,
            string name,
            byte[] signature,
            int bodyOffset)
            : base()
        {
            Attributes = attributes;
            ImplAttributes = implAttributes;
            Name = name;
            Signature = signature;
            BodyOffset = bodyOffset;
        }

        public MethodDefinitionRule(MetadataReader reader, MethodDefinitionHandle methodDefinitionHandle)
            : base(reader)
        {
            MethodDefinition methodDefinition = reader.GetMethodDefinition(methodDefinitionHandle);
            Attributes = methodDefinition.Attributes;
            ImplAttributes = methodDefinition.ImplAttributes;
            Name = reader.GetString(methodDefinition.Name);
            RelativeVirtualAddress = methodDefinition.RelativeVirtualAddress;
            Signature = reader.GetBlobBytes(methodDefinition.Signature);
            CustomAttributes = methodDefinition.GetCustomAttributes();
            DeclarativeSecurityAttributes = methodDefinition.GetDeclarativeSecurityAttributes();
            DeclaringType = methodDefinition.GetDeclaringType();
            GenericParameters = methodDefinition.GetGenericParameters();
            Import = methodDefinition.GetImport();
            Parameters = methodDefinition.GetParameters();
        }

        public override MethodDefinitionHandle Write(MetadataDesigner designer)
        {
            MetadataBuilder builder = designer.Builder;
            MethodDefinitionHandle methodDefinitionHandle = builder.AddMethodDefinition(
                attributes: Attributes,
                implAttributes: ImplAttributes,
                name: Name == null ? default : builder.GetOrAddString(Name),
                signature: Signature == null ? default : builder.GetOrAddBlob(Signature),
                bodyOffset: BodyOffset,
                parameterList: MetadataTokens.ParameterHandle(designer.ParameterNumber.First));
            designer.ParameterCounter.Reset();
            designer.ParameterNumber.Next();
            designer.MethodDefinitionNumber.Increment();
            return methodDefinitionHandle;
        }
    }
}